feat: add support for Firestore Pipeline#292
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces Firestore Pipeline operations to the google_cloud_firestore package, enabling server-side projections, expressions, aggregates, and vector search, complete with comprehensive E2E and unit tests. The review feedback highlights several key improvement opportunities: reverting a breaking change to the DistanceMeasure enum by converting values to lowercase locally within the pipeline execution, optimizing performance by extracting a frequently compiled regular expression into a file-level constant, and adding as well as exporting missing top-level comparison helpers (lessThan and greaterThan) to ensure API completeness.
Coverage Report✅ Coverage 71.95% meets 40% threshold Total Coverage: 71.95% Package Breakdown
Minimum threshold: 40% |
|
|
||
| /// The starting point for constructing Firestore Pipeline operations. | ||
| @immutable | ||
| final class PipelineSource { |
There was a problem hiding this comment.
There was a problem hiding this comment.
PipelineFunctions accepts Object? but only Expression.field() actually works without a fieldOrExpression-style check — untested since every call site already used field(). We should either commit to requiring Expression.field() explicitly or handle the coercion like Node's fieldOrExpression.
|
|
||
| /// STARTS_WITH string function. | ||
| static PipelineBooleanExpression startsWith(Object? value, Object? prefix) { | ||
| return _bool('starts_with', [value, prefix]); |
There was a problem hiding this comment.
Since value is typed as Object?, we should check at runtime whether it was passed as a String and wrap it with field(value) so it's encoded as a field reference.
Without that check, the string is encoded as a literal value instead of a field reference — so the expression compares against the fixed text 'title', not the value of the title field.
Example (Bug) — this looks like it filters on the title field, but instead compares the literal text "title" against "Harry":
await firestore
.pipeline()
.collection('books')
.where(PipelineFunctions.startsWith('title', 'Harry'))
.execute();This problem also exists in other methods across PipelineFunctions e.g. equal, lessThan, arrayContains, mapGet, and others
| } | ||
|
|
||
| /// STARTS_WITH string function. | ||
| static PipelineBooleanExpression startsWith(Object? value, Object? prefix) { |
There was a problem hiding this comment.
we should rename value to fieldName which is more descriptive and matches node.
| static PipelineBooleanExpression equal(Object? left, Object? right) { | ||
| return _bool('equal', [left, right]); | ||
| } |
There was a problem hiding this comment.
same issue here, we should check if left is String and wrap with field(left).
-
In node,
fieldOrExpressionhandles convertingleftto field if a string was passed:
https://github.com/googleapis/google-cloud-node/blob/main/handwritten/firestore/dev/src/pipelines/expression.ts#L5061-L5068 -
pipeline node tests (validates bug):
https://github.com/googleapis/google-cloud-node/blob/main/handwritten/firestore/dev/system-test/pipeline.ts#L1128-L1144
Add support for Firestore Pipeline
How to use
Aggregates
Aggregate stages use aliased aggregate expressions:
Expressions
Use
Expression.field,Expression.constant, andExpression.variabletobuild expressions. Most helpers are also available as fluent methods: